﻿/**
* CRL
*/
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using CRL.LambdaQuery;
namespace CRL.DBExtend.RelationDB
{

    public sealed partial class DBExtend
    {
        public override async Task InsertFromObjAsync<TModel>(TModel obj)
        {
            //var Reflection = ReflectionHelper.GetInfo<TModel>();
            CheckTableCreated<TModel>();
            var primaryKey = TypeCache.GetTable(obj.GetType()).PrimaryKey;
            object val;
            CheckData(obj, false);
            try
            {
                val = await _DBAdapter.InsertObjectAsync(dbContext, obj);
            }
            catch (Exception ero)
            {
                var ero2 = _DBAdapter.CheckFieldLength(dbContext, typeof(TModel), ero, new TModel[] { obj });
                throw ero2;
            }
            if (primaryKey != null && primaryKey.KeepIdentity == false)
            {
                //Reflection.GetAccessor(primaryKey.Name).Set((TModel)obj, index);
                val = ObjectConvert.ConvertObject(primaryKey.PropertyType, val);
                primaryKey.SetValue(obj, val);
            }
            ClearParame();
            //obj.SetOriginClone();
            UpdateCacheItem<TModel>(obj, null, true);
        }
        public override async Task BatchInsertAsync<TModel>(List<TModel> details, bool keepIdentity = false)
        {
            CheckTableCreated<TModel>();
            if (details.Count == 0)
                return;
            foreach (TModel item in details)
            {
                //item.CheckRepeatedInsert = false;
                CheckData(item, false);
            }
            try
            {
                await _DBAdapter.BatchInsertAsync(dbContext, details, keepIdentity);
            }
            catch (Exception ero)
            {
                var ero2 = _DBAdapter.CheckFieldLength(dbContext, typeof(TModel), ero, details);
                throw ero2;
            }
            var updateModel = MemoryDataCache.CacheService.GetCacheTypeKey(typeof(TModel), __DbHelper.DatabaseName);
            foreach (var item in details)
            {
                foreach (var key in updateModel)
                {
                    MemoryDataCache.CacheService.UpdateCacheItem(key, item, null);
                }
            }
        }
        public override async Task<List<TModel>> QueryOrFromCacheAsync<TModel>(ILambdaQuery<TModel> iQuery)
        {
            var query = iQuery as LambdaQueryBase;
            var cacheKey = "";
            CheckTableCreated<TModel>();
            List<TModel> list = new List<TModel>();
            if (query.HasPageArgs())//按分页
            {
                list = await QueryResultAsync<TModel>(query, null);
                return list;
            }
            //cacheKey = "";
            System.Data.Common.DbDataReader reader;
            query.FillParames(this);
            var sql = query.GetQuery();
            sql = _DBAdapter.SqlFormat(sql);
            var cacheTime = query.__ExpireMinute;
            var compileSp = query.__CompileSp && _DBAdapter.CanCompileSP;
            //double runTime = 0;
            var db = GetDBHelper(DataAccessType.Read);
            if (cacheTime <= 0)
            {
                if (!compileSp || !_DBAdapter.CanCompileSP)
                {
                    reader = await db.ExecDataReaderAsync(sql);
                }
                else//生成储过程
                {
                    string sp = CompileSqlToSp(_DBAdapter.TemplateSp, sql);
                    reader = await db.RunDataReaderAsync(sp);
                }
                query.ExecuteTime += db.ExecuteTime;
                var queryInfo = new LambdaQuery.Mapping.QueryInfo<TModel>(false, query.GetQueryFieldString(), query.GetFieldMapping());
                return await ObjectConvert.DataReaderToSpecifiedListAsync<TModel>(reader, queryInfo);
            }
            else
            {
                list = MemoryDataCache.CacheService.GetCacheList<TModel>(sql, query.GetFieldMapping(), cacheTime, db, out cacheKey).Values.ToList();
            }
            ClearParame();
            query.__RowCount = list.Count;
            //query = null;
            return list;
        }
        public override async Task<List<dynamic>> QueryDynamicAsync(LambdaQueryBase query)
        {
            CallBackDataReader reader;
            if (query.HasPageArgs())
            {
                reader = await GetPageReaderAsync(query);
            }
            else
            {
                reader = await GetQueryDynamicReaderAsync(query);
            }
            var result = await reader.GetDataDynamicAsync();
            var list = result.Item1;
            var count = result.Item2;
            if (count == 0)
            {
                count = list.Count;
            }
            query.MapingTime += reader.runTime;
            query.__RowCount = count;
            return list;
        }

        internal async Task<CallBackDataReader> GetQueryDynamicReaderAsync(LambdaQueryBase query)
        {
            CheckTableCreated(query.__MainType);
            var sql = "";
            query.FillParames(this);
            sql = query.GetQuery();
            sql = _DBAdapter.SqlFormat(sql);
            System.Data.Common.DbDataReader reader;
            var compileSp = query.__CompileSp && _DBAdapter.CanCompileSP;
            var db = GetDBHelper(DataAccessType.Read);
            if (!compileSp)
            {
                if (query.TakeNum > 0)
                {
                    db.AutoFormatWithNolock = false;
                }
                reader = await db.ExecDataReaderAsync(sql);
            }
            else//生成储过程
            {
                string sp = CompileSqlToSp(_DBAdapter.TemplateSp, sql);
                reader = await db.RunDataReaderAsync(sp);
            }
            query.ExecuteTime = db.ExecuteTime;
            ClearParame();
            return new CallBackDataReader(reader, null, sql);
        }

        public async override Task<List<TResult>> QueryResultAsync<TResult>(LambdaQueryBase query, NewExpression newExpression = null)
        {
            var anonymousClass = newExpression != null;
            List<TResult> list;
            var queryInfo = new LambdaQuery.Mapping.QueryInfo<TResult>(anonymousClass, query.GetQueryFieldString(), query.GetFieldMapping(), newExpression?.Constructor);
            CallBackDataReader reader;
            if (query.HasPageArgs())
            {
                reader = await GetPageReaderAsync(query);
            }
            else
            {
                reader = await GetQueryDynamicReaderAsync(query);
            }
            var result = await reader.GetDataTResultAsync(queryInfo);
            list = result.Item1;
            var count = result.Item2;
            if (count == 0)
            {
                count = list.Count;
            }
            query.__RowCount = count;
            return list;
        }

        internal async Task<CallBackDataReader> GetPageReaderAsync(LambdaQueryBase query1)
        {
            if (query1.__Unions != null)
            {
                return await GetUnionPageReaderAsync(query1);
            }
            else if (query1.__GroupFields != null)
            {
                return await GetGroupPageReaderAsync(query1);
            }
            var sql = GetPageReaderSql(query1, out var db, out var condition);
            string countSql = string.Format("select count(*) {0}", condition);
            int count = Convert.ToInt32(SqlStopWatch.ExecScalar(db, countSql));
            query1.ExecuteTime += db.ExecuteTime;
            query1.__RowCount = count;
            var reader = new CallBackDataReader(await db.ExecDataReaderAsync(sql), () =>
            {
                return count;
            }, sql);
            query1.ExecuteTime += db.ExecuteTime;
            ClearParame();
            return reader;
        }

        async Task<CallBackDataReader> GetGroupPageReaderAsync(LambdaQueryBase query1)
        {
            var sql = GetGroupPageReaderSql(query1, out var db, out var condition);
            string countSql = string.Format("select count(*)  from (select count(*) as a {0}) t", condition);
            //db = GetDBHelper(DataAccessType.Read);
            int count = Convert.ToInt32(SqlStopWatch.ExecScalar(db, countSql));
            query1.ExecuteTime += db.ExecuteTime;
            query1.__RowCount = count;
            var reader = new CallBackDataReader(await db.ExecDataReaderAsync(sql), () =>
            {
                return count;
            }, sql);
            query1.ExecuteTime += db.ExecuteTime;
            ClearParame();
            return reader;
        }
        async Task<CallBackDataReader> GetUnionPageReaderAsync(LambdaQueryBase query1)
        {
            //CheckTableCreated(query1.__MainType);
            var sql = GetUnionPageReaderSql(query1, out var db, out var condition);
            string countSql = string.Format("select count(*) {0}", condition);
            //db = GetDBHelper(DataAccessType.Read);
            int count = Convert.ToInt32(SqlStopWatch.ExecScalar(db, countSql));
            query1.ExecuteTime += db.ExecuteTime;
            query1.__RowCount = count;

            var reader = new CallBackDataReader(await db.ExecDataReaderAsync(sql), () =>
            {
                return count;
            }, sql);
            query1.ExecuteTime += db.ExecuteTime;
            ClearParame();
            return reader;
        }
        public override Task<int> ExecuteAsync(string sql)
        {
            sql = _DBAdapter.SqlFormat(sql);
            var db = GetDBHelper();
            sql = _DBAdapter.ReplaceParameter(db, out var _p, sql);
            var count = SqlStopWatch.ExecuteAsync(db, sql);
            ClearParame();
            return count;
        }
        internal Task<int> DeleteAsync<TModel>(string where)
        {
            CheckTableCreated<TModel>();
            string table = TypeCache.GetTableName(typeof(TModel), dbContext);
            string sql = _DBAdapter.GetDeleteSql(table, where);
            sql = _DBAdapter.SqlFormat(sql);
            var db = GetDBHelper();
            var n = SqlStopWatch.ExecuteAsync(db, sql);
            ClearParame();
            return n;
        }
        public override Task<int> DeleteAsync<T>(ILambdaQuery<T> query)
        {
            var query1 = query as RelationLambdaQuery<T>;
            if (query1.__GroupFields != null)
            {
                throw new Exception("delete不支持group查询");
            }
            if (query1.__Relations != null && query1.__Relations.Count > 1)
            {
                throw new Exception("delete关联不支持多次");
            }
            var conditions = Core.StringBuilderCache.GetSimpleString(sb =>
            {
                query1.GetQueryConditions(sb, false);
            });
            string table = query1.QueryTableName;
            table = _DBAdapter.KeyWordFormat(table);
            query1.FillParames(this);
            if (query1.__Relations != null)
            {
                var kv = query1.__Relations.First();
                var t1 = query1.QueryTableName;
                var t2 = TypeCache.GetTableName(kv.Key.OriginType, query1.__DbContext);
                string sql = _DBAdapter.GetRelationDeleteSql(t1, t2, conditions, query1);
                return ExecuteAsync(sql);
            }
            conditions = conditions.Replace("t1.", "");
            return DeleteAsync<T>(conditions);
        }

        internal Task<int> UpdateAsync<TModel>(ParameCollection setValue, string where)
        {
            CheckTableCreated<TModel>();
            Type type = typeof(TModel);
            string table = TypeCache.GetTableName(type, dbContext);
            string setString = ForamtSetValue<TModel>(setValue);
            string sql = _DBAdapter.GetUpdateSql(table, setString, where);
            sql = _DBAdapter.SqlFormat(sql);
            var db = GetDBHelper();
            var n = SqlStopWatch.ExecuteAsync(db, sql);
            ClearParame();
            return n;
        }

        public override Task<int> UpdateAsync<TModel>(ILambdaQuery<TModel> query, ParameCollection updateValue)
        {
            var query1 = query as RelationLambdaQuery<TModel>;
            if (query1.__GroupFields != null)
            {
                throw new Exception("update不支持group查询");
            }
            if (query1.__Relations != null && query1.__Relations.Count > 1)
            {
                throw new Exception("update关联不支持多次");
            }
            if (updateValue.Count == 0)
            {
                throw new ArgumentNullException("更新时发生错误,参数值为空 ParameCollection setValue");
            }

            var conditions = Core.StringBuilderCache.GetSimpleString(sb =>
            {
                query1.GetQueryConditions(sb, false);
            }).Trim();

            query1.FillParames(this);

            if (query1.__Relations != null)
            {
                var kv = query1.__Relations.First();
                string setString = ForamtSetValue<TModel>(updateValue, kv.Key.OriginType);
                var t1 = query1.QueryTableName;
                var t2 = TypeCache.GetTableName(kv.Key.OriginType, query1.__DbContext);

                string sql = _DBAdapter.GetRelationUpdateSql(t1, t2, conditions, setString, query1);
                return ExecuteAsync(sql);
            }
            else
            {
                conditions = conditions.Replace("t1.", "");
            }
            return UpdateAsync<TModel>(updateValue, conditions);
        }
    }
}
